//
// Created by root on 4/29/17.
//

#include <memory.h>
#include "../runner.h"

WRAP_SCHEME_OP2(int_add)
WRAP_SCHEME_OP2(int_mul)
WRAP_SCHEME_OP2(int_sub)
WRAP_SCHEME_OP2(int_div)
WRAP_SCHEME_OP2(fraction_add)
WRAP_SCHEME_OP2(fraction_mul)
WRAP_SCHEME_OP2(fraction_sub)
WRAP_SCHEME_OP2(fraction_div)
WRAP_SCHEME_OP2(decimal_add)
WRAP_SCHEME_OP2(decimal_mul)
WRAP_SCHEME_OP2(decimal_sub)
WRAP_SCHEME_OP2(decimal_div)

#define OP_REAL_INTEGER(_op,_x,_y,_z) Cell* arg_x = make_integer(global_scheme, _x); \
    Cell* arg_y = make_integer(global_scheme, _y); \
    Cell* ret = make_integer(global_scheme, _z); \
    expect_value(__wrap_int_##_op, sc, global_scheme); \
    expect_value(__wrap_int_##_op, x, arg_x); \
    expect_value(__wrap_int_##_op, y, arg_y); \
    will_return(__wrap_int_##_op, ret); \
    Cell* r = real_##_op \
    (global_scheme, arg_x, arg_y)

#define OP_REAL_FRACTION(_op,_a,_b,_c,_d,_e,_f) Cell* arg_x = \
    make_fraction(global_scheme, make_integer(global_scheme, _a), make_integer(global_scheme, _b)); \
    Cell* arg_y = make_fraction(global_scheme, make_integer(global_scheme, _c), make_integer(global_scheme, _d)); \
    Cell* ret = make_fraction(global_scheme, make_integer(global_scheme, _e), make_integer(global_scheme, _f)); \
    expect_value(__wrap_fraction_##_op, sc, global_scheme); \
    expect_value(__wrap_fraction_##_op, x, arg_x); \
    expect_value(__wrap_fraction_##_op, y, arg_y); \
    will_return(__wrap_fraction_##_op, ret); \
    Cell* r = real_##_op \
    (global_scheme, arg_x, arg_y)

#define OP_REAL_DECIMAL(_op,_x,_y,_z) Cell* arg_x = make_decimal(global_scheme, _x); \
    Cell* arg_y = make_decimal(global_scheme, _y); \
    Cell* ret = make_decimal(global_scheme, _z); \
    expect_value(__wrap_decimal_##_op, sc, global_scheme); \
    expect_value(__wrap_decimal_##_op, x, arg_x); \
    expect_value(__wrap_decimal_##_op, y, arg_y); \
    will_return(__wrap_decimal_##_op, ret); \
    Cell* r = real_##_op \
    (global_scheme, arg_x, arg_y)

#define TEST_OK  assert_true(eqn(r, ret))

/**
 * normal cases for real operation
 * */

//real_add
static void test_real_add_when_type_is_integer() {
    OP_REAL_INTEGER(add, 1, 2, 3);
    TEST_OK;
}

static void test_real_add_when_type_is_fraction() {
    OP_REAL_FRACTION(add, 1, 2, 3, 4, 5, 4);
    TEST_OK;
}

static void test_real_add_when_type_is_decimal() {
    OP_REAL_DECIMAL(add, 0.1, 0.2, 0.3);
    TEST_OK;
}

//real_mul
static void test_real_mul_when_type_is_integer() {
    OP_REAL_INTEGER(mul, 2, 3, 6);
    TEST_OK;
}

static void test_real_mul_when_type_is_fraction() {
    OP_REAL_FRACTION(mul, 1, 2, 3, 4, 3, 8);
    TEST_OK;
}

static void test_real_mul_when_type_is_decimal() {
    OP_REAL_DECIMAL(mul, 0.1, 0.2, 0.02);
    TEST_OK;
}

//real_sub
static void test_real_sub_when_type_is_integer() {
    OP_REAL_INTEGER(sub, 1, 2, -1);
    TEST_OK;
}

static void test_real_sub_when_type_is_fraction() {
    OP_REAL_FRACTION(sub, 1, 2, 3, 4, -1, 4);
    TEST_OK;
}

static void test_real_sub_when_type_is_decimal() {
    OP_REAL_DECIMAL(sub, 0.1, 0.2, -0.1);
    TEST_OK;
}

//real_div
static void test_real_div_when_type_is_integer() {
    OP_REAL_INTEGER(div, 4, 2, 2);
    TEST_OK;
}

static void test_real_div_when_type_is_fraction() {
    OP_REAL_FRACTION(div, 1, 2, 3, 4, 2, 3);
    TEST_OK;
}

static void test_real_div_when_type_is_decimal() {
    OP_REAL_DECIMAL(sub, 0.1, 0.2, 0.5);
    TEST_OK;
}

//TODO
int main(int argc, char **argv) {
    const struct CMUnitTest tests[] = {
            /*real_add*/
            cmocka_unit_test(test_real_add_when_type_is_integer),
            cmocka_unit_test(test_real_add_when_type_is_fraction),
            cmocka_unit_test(test_real_add_when_type_is_decimal),
            /*real_mul*/
            cmocka_unit_test(test_real_mul_when_type_is_integer),
            cmocka_unit_test(test_real_mul_when_type_is_fraction),
            cmocka_unit_test(test_real_mul_when_type_is_decimal),
            /*real_sub*/
            cmocka_unit_test(test_real_sub_when_type_is_integer),
            cmocka_unit_test(test_real_sub_when_type_is_fraction),
            cmocka_unit_test(test_real_sub_when_type_is_decimal),
            /*real_div*/
            cmocka_unit_test(test_real_div_when_type_is_integer),
            cmocka_unit_test(test_real_div_when_type_is_fraction),
            cmocka_unit_test(test_real_div_when_type_is_decimal),
    };
    return cmocka_run_group_tests(tests, setup_scheme, teardown_scheme);
}